package com.indi.gulimall.cart.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.indi.common.constant.CartConstant;
import com.indi.common.dto.SkuInfoDTO;
import com.indi.common.dto.UserInfoDTO;
import com.indi.common.utils.R;
import com.indi.gulimall.cart.feign.ProductFeignService;
import com.indi.gulimall.cart.interceptor.CartInterceptor;
import com.indi.gulimall.cart.service.CartService;
import com.indi.gulimall.cart.vo.CartVO;
import com.indi.gulimall.cart.vo.CartVO.CartItemVO;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Service("cartService")
public class CartServiceImpl implements CartService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private ProductFeignService productFeignService;

    @Resource
    private ThreadPoolExecutor executor;

    @Override
    public void addToCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {
        BoundHashOperations<String, Object, Object> operations = getCartOperations();

        if (operations.get(skuId.toString()) == null) {
            // 如果购物车没有此商品，则添加

            // 使用异步编排
            // 1.远程查询当前要添加的商品信息
            CartItemVO cartItemVO = new CartItemVO();

            CompletableFuture<Void> skuInfoFuture = CompletableFuture.runAsync(() -> {
                R r = productFeignService.info(skuId);
                if (r.getCode() == 0) {
                    SkuInfoDTO skuInfoDTO = r.getData("skuInfo", new TypeReference<SkuInfoDTO>() {
                    });
                    cartItemVO.setCheck(true);
                    cartItemVO.setSkuId(skuInfoDTO.getSkuId());
                    cartItemVO.setTitle(skuInfoDTO.getSkuTitle());
                    cartItemVO.setImage(skuInfoDTO.getSkuDefaultImg());
                    cartItemVO.setPrice(skuInfoDTO.getPrice());
                    cartItemVO.setCount(num);
                    cartItemVO.setTotalPrice(skuInfoDTO.getPrice().multiply(new BigDecimal(num)));
                }
            }, executor);


            // 2.查询销售属性的组合 信息 List<String> getSkuAttrValues(skuId)
            CompletableFuture<Void> saleAttrFuture = CompletableFuture.runAsync(() -> {
                R r = productFeignService.getSaleAttrNameAndValues(skuId);
                if (r.getCode() == 0) {
                    List<String> data = r.getData(new TypeReference<List<String>>() {
                    });
                    cartItemVO.setSkuAttr(data);
                }
            }, executor);

            CompletableFuture.allOf(skuInfoFuture, saleAttrFuture).get();
            String s = JSON.toJSONString(cartItemVO);
            operations.put(skuId.toString(), s);
        } else {
            // 购物车有此商品，则修改数量

            // 通过skuId，获取到对应的购物项，
            String res = (String) operations.get(skuId.toString());

            // 将redis中的json字符串，转回实体类
            CartItemVO oldCartItemVO = JSON.parseObject(res, CartItemVO.class);
            oldCartItemVO.setCount(oldCartItemVO.getCount() + num);
            oldCartItemVO.setTotalPrice(oldCartItemVO.getPrice().multiply(new BigDecimal(oldCartItemVO.getCount())));
            operations.put(skuId.toString(), JSON.toJSONString(oldCartItemVO));
        }
    }

    @Override
    public CartItemVO getCartItem(Long skuId) {
        BoundHashOperations<String, Object, Object> cartOperations = getCartOperations();
        // 获取skuId对应的json商品数据
        String str = (String) cartOperations.get(skuId.toString());
        CartItemVO cartItemVO = JSON.parseObject(str, CartItemVO.class);
        return cartItemVO;
    }

    @Override
    public CartVO getCart() throws ExecutionException, InterruptedException {
        CartVO cartVO = new CartVO();
        UserInfoDTO userInfoDTO = CartInterceptor.threadLocal.get();
        String cartKey = "";
        if (userInfoDTO.getUserId() != null) {
            // 已登录
            // 先获取临时用户的购物车，如果有数据，则调用添加购物车方法
            // 里面会得知用户已登录，最终会将数据全部添加到已登录的用户购物车里
            cartKey = CartConstant.CART_PREFEX + userInfoDTO.getUserKey();
            List<CartItemVO> tempCartItemVOs = getCartItems(cartKey);
            if (tempCartItemVOs != null && tempCartItemVOs.size() > 0) {
                for (CartItemVO itemVO : tempCartItemVOs) {
                    addToCart(itemVO.getSkuId(), itemVO.getCount());
                }
                // 全合并到用户购物车之后，删除临时购物车
                clearCart(cartKey);
            }

            // 最后再获取已登录用户的购物车数据
            cartKey = CartConstant.CART_PREFEX + userInfoDTO.getUserId();
            List<CartItemVO> signedCartItemVOs = getCartItems(cartKey);
            cartVO.setItems(signedCartItemVOs);
        } else {
            // 未登录，直接取出临时购物车的数据
            cartKey = CartConstant.CART_PREFEX + userInfoDTO.getUserKey();
            List<CartItemVO> cartItemVOs = getCartItems(cartKey);
            cartVO.setItems(cartItemVOs);
        }
        setAggs(cartVO);
        return cartVO;
    }

    @Override
    public void clearCart(String cartKey) {
        stringRedisTemplate.delete(cartKey);
    }

    @Override
    public void checkItem(Long skuId, Integer check) {
        // 更新redis中指定商品的选中状态
        BoundHashOperations<String, Object, Object> cartOperations = getCartOperations();
        String str = (String) cartOperations.get(skuId.toString());
        CartItemVO cartItemVO = JSON.parseObject(str, CartItemVO.class);
        cartItemVO.setCheck(check == 1);
        String s = JSON.toJSONString(cartItemVO);
        cartOperations.put(skuId.toString(), s);
    }

    @Override
    public void changeItemCount(Long skuId, Integer num) {
        BoundHashOperations<String, Object, Object> cartOperations = getCartOperations();
        String str = (String) cartOperations.get(skuId.toString());
        CartItemVO cartItemVO = JSON.parseObject(str, CartItemVO.class);
        cartItemVO.setCount(num);
        cartItemVO.setTotalPrice(cartItemVO.getPrice().multiply(new BigDecimal(num)));
        String s = JSON.toJSONString(cartItemVO);
        cartOperations.put(skuId.toString(), s);
    }

    @Override
    public void deleteItem(Long skuId) {
        BoundHashOperations<String, Object, Object> cartOperations = getCartOperations();
        cartOperations.delete(skuId.toString());
    }

    @Override
    public List<CartItemVO> getCurrentUserCartItems() {
        UserInfoDTO userInfoDTO = CartInterceptor.threadLocal.get();
        if (userInfoDTO.getUserId() != null) {
            List<CartItemVO> cartItems = getCartItems(CartConstant.CART_PREFEX + userInfoDTO.getUserId());
            cartItems = cartItems.stream().filter(CartItemVO::getCheck).map(cartItemVO -> {
                // 获取每个购物项的最新价格
                R r = productFeignService.info(cartItemVO.getSkuId());
                if (r.getCode() == 0) {
                    SkuInfoDTO skuInfoDTO = r.getData("skuInfo", new TypeReference<SkuInfoDTO>() {
                    });
                    cartItemVO.setPrice(skuInfoDTO.getPrice());
                }
                return cartItemVO;
            }).collect(Collectors.toList());
            return cartItems;
        }
        return null;
    }

    /**
     * 根据购物车key获取到指定购物车的商品数据
     *
     * @param cartKey
     * @return
     */
    private List<CartItemVO> getCartItems(String cartKey) {
        // 这个方法，即使redis中没有，也会在redis中创建出这条数据
        BoundHashOperations<String, Object, Object> cartOperations = stringRedisTemplate.boundHashOps(cartKey);
        List<Object> values = cartOperations.values();
        if (values != null && values.size() > 0) {
            List<CartItemVO> cartItemVOs = values.stream().map(obj -> JSON.parseObject((String) obj,
                    CartItemVO.class)).collect(Collectors.toList());
            return cartItemVOs;
        }
        return null;
    }

    /**
     * 计算购物车有多少种商品、所有商品的总数量、总价
     *
     * @param cartVO
     */
    private void setAggs(CartVO cartVO) {
        int countNum = 0;
        int countType = 0;
        BigDecimal amount = BigDecimal.ZERO;

        List<CartItemVO> items = cartVO.getItems();
        if (items != null && items.size() > 0) {
            for (CartItemVO item : items) {
                if (item.getCheck()) {
                    countNum += item.getCount();
                    countType += 1;
                    amount = amount.add(item.getTotalPrice());
                }
            }
        }
        BigDecimal finalTotalAmount = amount.subtract(cartVO.getReduce());
        cartVO.setCountNum(countNum);
        cartVO.setCountType(countType);
        cartVO.setTotalAmount(finalTotalAmount);
    }

    /**
     * 获取到要操作的购物车
     *
     * @return
     */
    private BoundHashOperations<String, Object, Object> getCartOperations() {
        UserInfoDTO userInfoDTO = CartInterceptor.threadLocal.get();
        String cartKey = "";
        if (userInfoDTO.getUserId() != null) {
            // 已登录用户的redis-key
            cartKey = CartConstant.CART_PREFEX + userInfoDTO.getUserId();
        } else {
            // 游客用户的redis-key
            cartKey = CartConstant.CART_PREFEX + userInfoDTO.getUserKey();
        }

        /*
            这个方法可以实现，以后所有对redis做的操作都只针对这一个key，
            用起来比更方便，不像opsForHash，每次使用都要传redis-key，和购物项的key
         */
        return stringRedisTemplate.boundHashOps(cartKey);
    }
}
